package cn.bearspark.common.aop.autoparse.host;

import cn.bearspark.common.parse.Pipeline;
import cn.bearspark.common.parse.host.HostParser;
import cn.bearspark.common.parse.host.HostPipeline;
import cn.bearspark.common.parse.host.HostTextContext;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;

/**
 * 自动(反)解析 host 注解的切面
 *
 * @author f
 */
@Aspect
@Component
public class AutoParseHostAspect {

    /**
     * 解析主机 ip 地址的管道
     */
    private final Pipeline<HostTextContext> hostPipeline = new HostPipeline()
            .add(new HostParser());

    /**
     * 自动(反)解析需要(反)解析 host 的字段
     * <p>
     * 目前：
     * 解析仅解析第一个参数内部的：单个对象, List
     * 反解析支持的返回值类型为：单个对象, List, Map
     *
     * @param joinPoint 切入点
     * @return 拥有(反)解析后的字段的对象
     * @throws Throwable 执行原始方法可能抛出的异常
     */
    @Around("@annotation(cn.bearspark.common.aop.autoparse.host.AutoParseHost)")
    public Object autoParse(ProceedingJoinPoint joinPoint) throws Throwable {
        List<Field> fieldsToParse = getFieldsToParse(joinPoint);
        ParseEnum parse = fieldsToParse.get(0).getAnnotation(ParseHost.class).value();

        if (parse == ParseEnum.PARSE) {
            Object[] args = joinPoint.getArgs();
            Object argToParse = args[0];
            for (Field field : fieldsToParse) {
                Object fieldToParse = field.get(argToParse);
                if (fieldToParse instanceof List<?> list) {
                    if (list.isEmpty()) {
                        continue;
                    }
                    field.set(argToParse, list.stream().map(e -> hostPipeline.parse((String) e).getBody()).toList());
                } else if (fieldToParse != null) {
                    field.set(argToParse, hostPipeline.parse((String) fieldToParse).getBody());
                }
            }
            return joinPoint.proceed(args);
        } else {
            Object result = joinPoint.proceed();
            if (result instanceof List<?> list) {
                if (list.isEmpty()) {
                    return result;
                }
                for (Object e : list) {
                    unparseFields(e, fieldsToParse);
                }
            } else if (result instanceof Map<?, ?> map) {
                if (map.isEmpty()) {
                    return result;
                }
                Collection<?> values = map.values();
                for (Object value : values) {
                    if (value instanceof List<?> list) {
                        if (list.isEmpty()) {
                            return result;
                        }
                        for (Object e : list) {
                            unparseFields(e, fieldsToParse);
                        }
                    } else if (value != null) {
                        unparseFields(value, fieldsToParse);
                    }
                }
            } else if (result != null) {
                unparseFields(result, fieldsToParse);
            }
            return result;
        }
    }

    // 获取需要解析的字段
    private static List<Field> getFieldsToParse(ProceedingJoinPoint joinPoint) {
        List<Field> fieldsToParse = new ArrayList<>();
        Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
        Class<?> classToUnparse = method.getAnnotation(AutoParseHost.class).value();
        for (Field field : classToUnparse.getDeclaredFields()) {
            if (field.isAnnotationPresent(ParseHost.class)) {
                field.setAccessible(true);
                fieldsToParse.add(field);
            }
        }
        return fieldsToParse;
    }

    // 反解析每个需要反解析的字段
    private void unparseFields(Object e, List<Field> fieldsToUnparse) throws IllegalAccessException {
        for (Field field : fieldsToUnparse) {
            Object object = field.get(e);
            if (object instanceof List<?> list) {
                if (list.isEmpty()) {
                    return;
                }
                field.set(e, list.stream().map(o -> hostPipeline.parse((String) o).getBody()).toList());
            } else {
                field.set(e, hostPipeline.unparse(new HostTextContext((String) object)));
            }
        }
    }

}
